﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;

namespace BitTorrent.Bencode.Collections {

    internal class Dictionary : Object, IDictionary<ByteString, Object> {
        private readonly SortedDictionary<ByteString, Object> _dictonary = new SortedDictionary<ByteString, Object>();

        public Object this[string key] {
            get {
                Contract.Requires(key != null);

                return _dictonary[(ByteString)key];
            }
            set {
                Contract.Requires(key != null);
                Contract.Requires(value != null);

                _dictonary[(ByteString)key] = value;
            }
        }

        public ICollection<ByteString> Keys {
            get { return _dictonary.Keys; }
        }

        public ICollection<Object> Values {
            get { return _dictonary.Values; }
        }

        public Object this[ByteString key] {
            get {
                Contract.Requires(key != null);

                return _dictonary[key]; 
            }
            set {
                Contract.Requires(key != null);
                Contract.Requires(value != null);

                _dictonary[key] = value; 
            }
        }

        public void Add(ByteString key, Object value) {
            Contract.Requires(key != null);
            Contract.Requires(value != null);

            _dictonary.Add(key, value);
        }

        public bool ContainsKey(ByteString key) {
            Contract.Requires(key != null);

            return _dictonary.ContainsKey(key);
        }

        public bool Remove(ByteString key) {
            Contract.Requires(key != null);

            return _dictonary.Remove(key);
        }

        public bool TryGetValue(ByteString key, out Object value) {
            Contract.Requires(key != null);

            return _dictonary.TryGetValue(key, out value);
        }

        public int Count {
            get { return _dictonary.Count; }
        }

        public bool IsReadOnly {
            get { return false; }
        }

        public void Add(KeyValuePair<ByteString, Object> item) {
            Contract.Requires(item.Key != null);
            Contract.Requires(item.Value != null);

            _dictonary.Add(item.Key, item.Value);
        }

        public void Clear() {
            _dictonary.Clear();
        }

        public bool Contains(KeyValuePair<ByteString, Object> item) {
            Contract.Requires(item.Key != null);
            Contract.Requires(item.Value != null);

            return _dictonary.Contains(item);
        }

        public void CopyTo(KeyValuePair<ByteString, Object>[] array, int arrayIndex) {
            Contract.Requires(array != null);
            Contract.Requires(arrayIndex > 0);
            Contract.Requires(arrayIndex < array.Length);

            _dictonary.CopyTo(array, arrayIndex);
        }

        public bool Remove(KeyValuePair<ByteString, Object> item) {
            Contract.Requires(item.Key != null);
            Contract.Requires(item.Value != null);

            return _dictonary.Remove(item.Key);
        }

        public IEnumerator<KeyValuePair<ByteString, Object>> GetEnumerator() {
            return _dictonary.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }

        internal override int ByteSize {
            get {
                Contract.Ensures(Contract.Result<int>() > 0);

                int size = Encoding.UTF8.GetByteCount("d");

                foreach (ByteString key in _dictonary.Keys) {
                    size += key.ByteSize;
                    size += _dictonary[key].ByteSize;
                }

                size += Encoding.UTF8.GetByteCount("e");

                return size;
            }
        }

        internal override int Encode(byte[] data, int offset) {
            Contract.Requires(data != null);
            Contract.Requires(offset > 0);
            Contract.Requires(offset < data.Length);

            int written = Encoding.UTF8.GetBytes("d", 0, 1, data, offset);

            foreach (var key in _dictonary.Keys) {
                written += key.Encode(data, offset + written);
                written += _dictonary[key].Encode(data, offset + written);
            }

            written += Encoding.UTF8.GetBytes("e", 0, 1, data, offset + written);

            return written;
        }

        public bool ContainsKey(string key) {
            Contract.Requires(key != null);
            Contract.Requires(key.Length > 0);

            return _dictonary.ContainsKey((ByteString)key);
        }
    }
}
